/**
 * @file task.c
 * @author lcz (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-22
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "core/task.h"
#include "core/memory.h"
#include "core/syscall.h"
#include "comm/cpu_instr.h"
#include "tools/log.h"
#include "os.cfg.h"
#include "tools/kilb.h"
#include "cpu/cpu.h"
#include "cpu/mmu.h"
#include "comm/elf.h"
#include "fs/fs.h"

static task_mananger_t task_manager;

static task_t task_table[TASK_NR];
static mutex_t task_table_mutex;
//file_t *file_table[TASK_OFILE_NR]; 

/**
 * @brief tss 初始化
 *
 * @param task
 * @param entry
 * @param esp
 * @return * int
 */
static int
tss_init(task_t *task, uint32_t entry, uint32_t esp, int flags)
{
    int tss_sel = gdt_alloc_desc();
    if (tss_sel < 0)
    {
        log_printf("gdt used over!!");
        return -1;
    }

    segment_desc_set(tss_sel, (uint32_t)&task->tss, sizeof(tss_t),
                     SEG_P_PRESENT | SEG_DPL0 | SEG_TYPR_TSS);

    kernel_memset(&task->tss, 0, sizeof(tss_t));
    // 这里设置进程的段选择子，后续时cs及ss，他们需要有相应的rpl及cpl好吧
    int code_sel, data_sel;
    // 选择子
    // 宏定义SEG_RPL3
    // 我们将code_sel data_sel写到tss寄存器中，切换时执行相应的权限
    // 任务初始化，看看是不是系统任务
    if (flags & TASK_FLAGS_SYSTEM)
    {
        // 特权级0的初始化
        code_sel = KERNEL_SELECTOR_CS;
        data_sel = KERNEL_SELECTOR_DS;
    }
    else
    {
        // cpl3的初始化，特权级3的初始化
        code_sel = task_manager.app_code_sel | SEG_RPL3;
        data_sel = task_manager.app_data_sel | SEG_RPL3;
    }
    // 很奇怪，内核栈的栈空间并不在内核中好像，而在内核外，当然线性地址在0x8000 0000 以上
    uint32_t kernel_stack = memory_alloc_page();
    if (kernel_stack == 0)
    {
        goto tss_init_failed;
    }

    task->tss.eip = entry;
    task->tss.esp = esp ? esp : kernel_stack + MEM_PAGE_SIZE; // 未指定栈则用内核栈，即运行在特权级0的进程
    task->tss.esp0 = kernel_stack + MEM_PAGE_SIZE;
    task->tss.ss0 = KERNEL_SELECTOR_DS;
    task->tss.eip = entry;
    task->tss.eflags = EFLAGS_DEFAULT | EFLAGS_IF;
    task->tss.es = task->tss.ss = task->tss.ds = task->tss.fs = task->tss.gs = data_sel; // 全部采用同一数据段
    task->tss.cs = code_sel;
    // task->tss.iomap = 0;
    // task->tss.eip = entry;
    // task->tss.esp = esp ? esp : kernel_stack + MEM_PAGE_SIZE; // 未指定栈则用内核栈，即运行在特权级0的进程
    // task->tss.esp0 = kernel_stack + MEM_PAGE_SIZE;
    // task->tss.ss = data_sel;
    // task->tss.ss0 = KERNEL_SELECTOR_DS;
    // task->tss.es = task->tss.ds = task->tss.fs = task->tss.gs = data_sel;
    // task->tss.cs = code_sel;
    // task->tss.eflags = EFLAGS_DEFAULT | EFLAGS_IF;
    // 初始化tss的cr3 页目录地址
    uint32_t page_dir = memory_create_uvm();
    if (page_dir == 0)
    {
        // 释放选择子，进程初始化失败，回滚以下
        goto tss_init_failed;
    }
    task->tss.cr3 = page_dir;

    // io_map ssp ldt 不初始化
    // task->tss.io_map =32;
    // task->tss.ssp=32;
    // task->tss.ldt=32;
    task->tss_sel = tss_sel;
    return 0;
tss_init_failed:
    gdt_free_sel(tss_sel);
    if (kernel_stack)
    {
        memory_free_page(kernel_stack);
    }
    return -1;
}

// idle process stack
static uint32_t idle_task_stack[IDLE_TASK_STACK_SIZE];

/**
 * @brief idle precess execute code, waitting and low power running（空闲进程执行的内容函数）
 *
 * @return * void
 */
void idle_task_entry(void)
{
    for (;;)
    {
        hlt();
    }
}
/**
 * @brief 将任务插入就绪队列
 */
void task_set_ready(task_t *task)
{
    if (&task_manager.idle_task == task)
    {
        return;
    }

    list_insert_last(&task_manager.ready_list, &(task->run_node));
    task->state = TASK_READY;
}

/**
 * @brief task init 初始化任务结构体
 *
 * @param task task struct
 * @param entry address of function which will execute by this  task
 * @param esp   task stack address
 * @param name 进程名称
 * @param flags 系统任务值为1，初始化高特权级
 * @return * int
 */
int task_init(task_t *task, uint32_t entry, uint32_t esp, char *name, int flags)
{
    ASSERT(task != (task_t *)0);
    // 硬件切换任务（进程）
    tss_init(task, entry, esp, flags);
    // 手动切换任务（进程）
    // init by hand
    // uint32_t *pesp = (uint32_t *)esp;
    // if (pesp)
    // {
    //     *(--pesp) = entry;
    //     *(--pesp) = 0;
    //     *(--pesp) = 0;
    //     *(--pesp) = 0;
    //     *(--pesp) = 0;
    //     task->stack = pesp;
    // }

    // init list_node_t in task struct and task_manager

    kernel_strncpy(task->name, name, TASK_NAME_SIZE);
    task->time_ticks = TASK_TIME_SLICE_DEFAULT;
    task->slice_ticks = task->time_ticks;
    task->sleep_ticks = 0;
    task->parent = (task_t *)0;
    task->state = TASK_CREATED;
    task->pid = (uint32_t)task;
    task->heap_start=0;
    task->heap_end = 0;
    task->status=0;
    list_node_init(&(task->all_node));
    list_node_init(&(task->run_node));
    list_node_init(&(task->wait_node));
    kernel_memset(&task->file_table,0,sizeof(task->file_table));

    irq_state_t state = irq_enter_protection();
    task->pid = (uint32_t)task;
    list_insert_last(&task_manager.task_list, &(task->all_node));
    irq_leave_protection(state);

    return 0;
}
/**
 * @brief 启动任务
 */
void task_start(task_t *task)
{
    //开关中断保护
    irq_state_t state = irq_enter_protection();
    task_set_ready(task);
    irq_leave_protection(state);
}
/**
 * @brief 切换至指定任务
 */
void task_switch_from_to(task_t *from, task_t *to)
{
    // 硬件切换任务（进程）
    switch_to_tss(to->tss_sel);
    // 手动切换任务（进程）
    // simple_switch(&(from->stack), to->stack);
}
/**
 * @brief 切换至指定任务
 */
void switch_to_tss(int tss_sel)
{
    far_jump(tss_sel, 0);
}
/**
 * @brief 任务管理器初始化
 */
void task_manager_init(void)
{
    kernel_memset(task_table, 0, sizeof(task_table));
    mutex_init(&task_table_mutex);

    // 初始哈特权级为3的段描述符
    int sel = gdt_alloc_desc();
    segment_desc_set(sel, 0x00000000, 0xffffffff, SEG_P_PRESENT | SEG_DPL3 | SEG_S_NORMAL | SEG_TYPE_DATA | SEG_TYPE_RW | SEG_D);
    task_manager.app_data_sel = sel;

    sel = gdt_alloc_desc();
    segment_desc_set(sel, 0x00000000, 0xffffffff, SEG_P_PRESENT | SEG_DPL3 | SEG_S_NORMAL | SEG_TYPE_CODE | SEG_TYPE_RW | SEG_D);
    task_manager.app_code_sel = sel;

    list_init(&task_manager.ready_list);
    list_init(&task_manager.task_list);
    list_init(&task_manager.sleep_list);
    // init process idle
    task_init(&task_manager.idle_task, (uint32_t)idle_task_entry,
              (uint32_t)(idle_task_stack + IDLE_TASK_STACK_SIZE), "idle process", 1);
    task_start(&(task_manager.idle_task));
    task_manager.curr_task = (task_t *)0;
}
/**
 * @brief 初始进程的初始化
 * 没有采用从磁盘加载的方式，因为需要用到文件系统，并且最好是和kernel绑在一定，这样好加载
 * 当然，也可以采用将init的源文件和kernel的一起编译。此里要调整好kernel.lds，在其中
 * 将init加载地址设置成和内核一起的，运行地址设置成用户进程运行的高处。
 * 不过，考虑到init可能用到newlib库，如果与kernel合并编译，在lds中很难控制将newlib的
 * 代码与init进程的放在一起，有可能与kernel放在了一起。
 * 综上，最好是分离。
 */
void task_first_init(void)
{
    extern uint8_t s_first_task[], e_first_task[];
    uint32_t copy_size = (uint32_t)(e_first_task - s_first_task);
    // 我们分配 40k 10页够用
    uint32_t alloc_size = 10 * MEM_PAGE_SIZE;
    ASSERT(copy_size < alloc_size);

    void first_task_entry(void);
    uint32_t first_start = (uint32_t)first_task_entry;
    // task_init 函数入口地址修改以下拜托
    // 传入的esp的值为特权级3的栈空间的值
    task_init(&task_manager.first_task, first_start, first_start + alloc_size, "first task", 0);
    // e_first_task 为 e_first_task 的物理结束地址
    task_manager.first_task.heap_start = (uint32_t)e_first_task;
    task_manager.first_task.heap_end = (uint32_t)e_first_task;
    write_tr(task_manager.first_task.tss_sel);
    task_manager.curr_task = &(task_manager.first_task);

    mmu_set_page_dir(task_manager.first_task.tss.cr3);
    // 现在页表切换了，进程页切换了，就进入了新进程了，

    // 分配一页内存供代码存放使用，然后将代码复制过去
    // 将first_task 的虚拟地址映射到对应的对应的物理页中
    // 将逻辑映射到对应的物理地址好吧！！！
    memory_alloc_page_for(first_start, alloc_size, PTE_P | PTE_W | PTE_U);
    // 将物理地址实实在在的s_first_task处代码复制到first_start虚拟地址处，要经过映射关系的好吧
    // s_first_task映射到本身还记得128M都是映射到本身码
    // first_start映射到实际地址的1M以上，就是真实的物理地址了，减这段代码放在物理地址上，但逻辑上它在0x8000 0000
    // 逻辑上我们页是将该地址放在0x8000 0000 所以复制的时候用的都是逻辑地址，没有什么问题好吧
    kernel_memcpy((void *)first_start, s_first_task, copy_size);
    task_start(&(task_manager.first_task));
}

/**
 * @brief 返回初始任务
 */
task_t *task_first_task(void)
{
    return &(task_manager.first_task);
}

/**
 * @brief 将任务从就绪队列移除(running 进程也在就绪队列中)
 */
void task_set_block(task_t *task)
{
    if (&task_manager.idle_task == task)
    {
        return;
    }

    list_remove(&task_manager.ready_list, &task->run_node);
    // task->state = TASK_WAITTING;
}
/**
 * @brief 获取当前正在运行的任务
 */
task_t *task_current(void)
{
    return task_manager.curr_task;
}
/**
 * @brief 获取下一将要运行的任务
 */
task_t *task_next_run(void)
{
    if (list_count(&(task_manager.ready_list)) == 0)
    {
        return &(task_manager.idle_task);
    }

    list_node_t *task_node = list_first(&task_manager.ready_list);
    // 获取下一个目标任务的基地址，转换为任务体结构task
    return list_node_parent(task_node, task_t, run_node);
}

/**
 * @brief 进行一次任务调度
 */
void task_dispatch(void)
{
    irq_state_t state = irq_enter_protection();
    task_t *task_to = task_next_run();
    // 如果只有切换的任务与当前任务是同一个，不切换
    if (task_to != task_manager.curr_task)
    {
        //运行到这里当前任务的指针已经不在最前面了，及已经将curr_task放在队列末尾了，sys_sched_yield中做的
        // 如果没有进入循环的话需要特别留意一下
        // task_current 仍指向运行进程
        task_t *task_from = task_current();
        task_manager.curr_task = task_to;
        task_to->state = TASK_RUNNING;
        task_switch_from_to(task_from, task_to);
    }
    irq_leave_protection(state);
}
/**
 * @brief 当前任务主动放弃CPU
 */
int sys_sched_yield(void)
{
    irq_state_t state = irq_enter_protection();
    if (list_count(&task_manager.ready_list) > 0)
    {
        task_t *curr_task = task_current();
        // first set bolck and set ready(let curr_task in last of list)
        task_set_block(curr_task);
        task_set_ready(curr_task);

        task_dispatch();
    }
    irq_leave_protection(state);
    return 0;
}
/**
 * @brief 进程退出，资源由父进程回收
 * @param status 退出状态码
 */
void sys_exit(int status)
{
    // 进程不返回 我们要杀死进程
    task_t *curr_task = task_current();
    // 关闭文件
    for (int fd = 0; fd < TASK_OFILE_NR; fd++)
    {
        file_t *file = curr_task->file_table[fd];
        if (file)
        {
            sys_close(fd);
            curr_task->file_table[fd] = (file_t *)0;
        }
    }

    // 锁保护
    int move_child = 0;
    mutex_lock(&task_table_mutex);
    for (int i = 0; i < TASK_NR; i++)
    {
        task_t *task = task_table + i;
        if (task->parent == curr_task)
        {
            task->parent = &task_manager.first_task;
            // 如果子进程中有僵尸进程，唤醒回收资源
            // 并不由自己回收，因为自己将要退出
            if (task->state == TASK_ZOMBIE)
            {
                // 有子进程也处于僵死状态
                move_child = 1;
            }
        }
    }
    mutex_unlock(&task_table_mutex);

    // 开始保护
    irq_state_t state = irq_enter_protection();
    curr_task->status = status;
    curr_task->state = TASK_ZOMBIE;
    task_set_block(curr_task);

    task_t *parent = curr_task->parent;
    // 即使此处我们若parent不为init进程，first_task,我们要唤醒父进程及init进程，一个处理exit的进程资源回收
    // 一个处理僵死的孤儿进程的回收
    if (move_child && (parent != &task_manager.first_task))
    { // 如果父进程为init进程，在下方唤醒
        if (task_manager.first_task.state == TASK_WAITTING)
        {
            task_set_ready(&task_manager.first_task);
        }
    }

    // 父进程在等子进程，我们要唤醒父进程一下
    if (parent->state == TASK_WAITTING)
    {
        task_set_ready(curr_task->parent);
    }
    // 孤儿进程没有处理 我们将孤儿进程进行转交

    task_dispatch();
    irq_leave_protection(state);
}
/**
 * @brief wait系统调用的处理函数，检测子进程并释放资源
 */
int sys_wait(int *status)
{
    task_t *curr_task = task_current();
    // 死循环等待进程退出
    for (;;)
    {
        // 锁保护一下
        mutex_lock(&task_table_mutex);
        for (int i = 0; i < TASK_NR; i++)
        {
            task_t *task = task_table + i;
            if (task->parent != curr_task)
            {
                continue;
            }
            // 存在子进程且其状态不为僵死同样进入等待状态
            if (task->state == TASK_ZOMBIE)
            {
                int pid = task->pid;
                *status = task->status;
                // 清除内存的映射关系,释放申请的内存页,好好理解一下 （摧毁页目录页表就重新可以使用了）
                memory_destory_uvm(task->tss.cr3);
                // 释放栈空间
                memory_free_page(task->tss.esp0 - MEM_PAGE_SIZE);
                kernel_memset(task,0,sizeof(task_t));
                mutex_unlock(&task_table_mutex);
                return pid;
            }
        }
        mutex_unlock(&task_table_mutex);
        // 进入等待状态的进程将释放僵死的子进程的资源
        // 若没有僵死的子进程则进入等待队列
        irq_state_t state = irq_enter_protection();
        task_set_block(curr_task);
        curr_task->state = TASK_WAITTING;
        task_dispatch();
        irq_leave_protection(state);
    }
    return 0;
    // 让进程自己去释放自己的资源，有资源运行程序，运行程序要释放所有资源，简直矛盾
}

/**
 * @brief 时间处理
 * 该函数在中断处理函数中调用
 */
void task_time_tick(void)
{
    task_t *curr_task = task_current();
    if (--curr_task->slice_ticks == 0)
    {

        curr_task->slice_ticks = curr_task->time_ticks;
        task_set_block(curr_task);
        task_set_ready(curr_task);
        task_dispatch();
    }

    // scanning sleep list
    int flags = 0;
    list_node_t *curr = list_first(&task_manager.sleep_list);
    while (curr)
    {
        list_node_t *next = list_node_next(curr);
        task_t *task = list_node_parent(curr, task_t, run_node);
        // curr=task->run_node.next;
        if (--task->sleep_ticks == 0)
        {
            flags = 1;
            task_set_wakeup(task);
            task_set_ready(task);
        }
        curr = next;
    }
    // 如果睡眠的进程加入就绪队列，那么就重新调度
    if (flags)
    {
        task_dispatch();
    }
}

/**
 * @brief 将任务加入睡眠状态
 */
void task_set_sleep(task_t *task, uint32_t ticks)
{
    if (ticks <= 0)
    {
        return;
    }
    task->sleep_ticks = ticks;
    task->state = TASK_SLEEP;
    list_insert_last(&task_manager.sleep_list, &task->run_node);
}
/**
 * @brief 将任务从延时队列移除
 *
 * @param task
 */
void task_set_wakeup(task_t *task)
{
    list_remove(&task_manager.sleep_list, &task->run_node);
    task->state = TASK_READY;
}
/**
 * @brief 任务进入睡眠状态
 *
 * @param ms
 */
void sys_sleep(uint32_t ms)
{
    irq_state_t state = irq_enter_protection();
    // 再此处task_set_block将当前任务从就绪队列移除，再移入到睡眠队列中
    // 因此task_set_sleep函数中不在减队列从就绪队列中移除，直接加入睡眠队列
    task_set_block(task_manager.curr_task);
    uint32_t ticks = (ms + (OS_TICKS_MS - 1)) / OS_TICKS_MS;
    task_set_sleep(task_manager.curr_task, ticks);
    task_dispatch();
    irq_leave_protection(state);
}

/**
 * @brief @brief 系统调用getpid的实现函数
 *
 * @return * int 进程号
 */
int sys_getpid(void)
{
    task_t *curr = task_current();
    return curr->pid;
}

// 用任务名判断是否存在有点奇怪
/**
 * @brief 从任务数组申请一个任务，
 *
 * @return * task_t* 申请到的任务
 */
static task_t *alloc_task(void)
{
    
    task_t *task = (task_t *)0;
    mutex_lock(&task_table_mutex);
    for (int i = 0; i < TASK_NR; i++)
    {
        task_t *curr = task_table + i;
        if (curr->name[0] == 0)
        {
            task = curr;
            break;
        }
    }
    mutex_unlock(&task_table_mutex);
    return task;
}
/**
 * @brief 从任务数组中释放指定的任务，进程
 *
 * @param task 将释放的任务
 * @return * void
 */
static void free_task(task_t *task)
{
    mutex_lock(&task_table_mutex);
    // 修改名称为空即可
    task->name[0] = '\0';
    mutex_unlock(&task_table_mutex);
}
/**
 * @brief 任务任务初始时分配的各项资源
 */
void task_uninit(task_t *task)
{
    if (task->tss_sel)
    {
        gdt_free_sel(task->tss_sel);
    }
    // 特权级为0的栈
    if (task->tss.esp0)
    {
        memory_free_page(task->tss.esp - MEM_PAGE_SIZE);
    }
    if (task->tss.cr3)
    {
        memory_destory_uvm(task->tss.cr3);
    }

    kernel_memset(task, 0, sizeof(task_t));
}

// 我们利用fork创子进程   fork中我们进行了task_init的初始化，task_init中我们清空了文件表，所以我们没有打开文件，没有打开设备，我们在初始化时需要
// fork中我们要复制父进程的文件表，
// tty:0 - stdin stdout stderr --shell
// 我们需要使文件的应用计数加1
// 注意我们的stdin stdout stderr均指向的tty:0[1-7]文件
static void copy_opened_file(task_t *child_task)
{
    task_t *parent = task_current();
    for (int i = 0; i < TASK_OFILE_NR; i++)
    {
        file_t *file = parent->file_table[i];
        if (file)
        {
            file_inc_ref(file);
            child_task->file_table[i] = file;
        }
    }
}

/**
 * @brief 创建子进程
 *
 * @return * int 父进程返回子进程id，子进程返回0
 */
int sys_fork(void)
{
    task_t *parent_task = task_current();
    task_t *child_task = alloc_task();
    if (child_task == (task_t *)0)
    {
        goto fork_failed;
    }
    // 利用父进程在特权级3压栈的内容，取到栈的起始地址，再利用C语言解析为对应的结构体syscall_frame_t
    // 进程进入特权级3时候的栈状态不一定是这个，但运行到这里的时候他的栈一定为这个样子
    // syscall_frame_t *frame = (syscall_frame_t *)parent_task->tss.esp0 - sizeof(syscall_frame_t);
    syscall_frame_t *frame = (syscall_frame_t *)(parent_task->tss.esp0 - sizeof(syscall_frame_t));

    // 此处注意一下
    // int err = task_init(child_task, parent_task->name, 0, frame->eip,
    //                     frame->esp + sizeof(uint32_t) * SYSCALL_PARAM_COUNT);
    // if (err < 0)
    // {
    //     goto fork_failed;
    // }
    int err = task_init(child_task, frame->eip, frame->esp + sizeof(uint32_t) * SYSCALL_PARAM_COUNT, parent_task->name, 0);
    if (err < 0)
    {
        goto fork_failed;
    }
    copy_opened_file(child_task);

        // uint32_t pre_link;
        // uint32_t esp0, ss0, esp1, ss1, esp2, ss2;
        // uint32_t cr3;
        // uint32_t eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
        // uint32_t es, cs, ss, ds, fs, gs;
        // uint32_t ldt;
        // uint32_t io_map;

        tss_t *tss = &child_task->tss;
    // 父子进程的返回值不同
    tss->eax = 0;
    tss->ebx = frame->ebx;
    tss->ecx = frame->ecx;
    tss->edx = frame->edx;
    tss->esi = frame->esi;
    tss->edi = frame->edi;
    tss->ebp = frame->ebp;

    tss->cs = frame->cs;
    tss->ds = frame->ds;
    tss->es = frame->es;
    tss->fs = frame->fs;
    tss->gs = frame->gs;
    tss->eflags = frame->eflags;

    child_task->parent = parent_task;
    // tss->cr3 = parent_task->tss.cr3;
    // 复制父进程的页目录给自进程，子进程进行重新映射，注意复制的是物理页的内容，此函数返回的目录页地址为新申请的，
    // 及子进程与父进程不使用同一目录页了，分开
    if ((child_task->tss.cr3 = memory_copy_uvm(parent_task->tss.cr3)) < 0)
    {
        goto fork_failed;
    }

    // 父子进程的返回值不同
    // child_task->tss.cr3 =parent_task->tss.cr3;
    task_start(child_task);
    return child_task->pid;
fork_failed:
    if (child_task)
    {
        task_uninit(child_task);
        free_task(child_task);
    }
    return -1;
}

// 传进去的页表与当前的页表不是同一个页表
// memory_alloc_for_page_dir 添加到头文件中
/**
 * @brief 加载程序
 *
 * @param file 文件标识
 * @param phdr 程序的物理地址（elf文件的phdr）
 * @param pafe_dir 建立的页目录地址
 * @return * int
 */
static int load_phdr(int file, Elf32_Phdr *phdr, uint32_t page_dir)
{
    // 直接可写，代码数据同一个段
    int err = memory_alloc_for_page_dir(page_dir, phdr->p_vaddr, phdr->p_memsz, PTE_P | PTE_U | PTE_W);
    // check err and return -1
    if (err < 0)
    {
        log_printf("memory_alloc_for_page_dir failed: %d\n", err);
        return -1;
    }

    if (sys_lseek(file, phdr->p_offset, 0) < 0)
    {
        log_printf("sys_lseek failed");
        return -1;
    }
    // 虚拟地址 elf中已经定义到了加载到的线性地址
    uint32_t vaddr = phdr->p_vaddr;
    // 程序大小
    uint32_t size = phdr->p_filesz;
    // 循环赋值
    while (size > 0)
    {
        int curr_size = (size > MEM_PAGE_SIZE) ? MEM_PAGE_SIZE : size;
        // 拷贝还不可以 页表还没有启用,这里的page_dir一直可以追述到sys_execve函数，系统调用，页表还没有启用，只能先映射了
        // 每次最多拷贝一直接
        uint32_t paddr = memory_get_paddr(page_dir, vaddr);
        // 0x1000 - 0x1000
        // 物理地址与 现在就是给一个页目录，给一个虚拟地址，放回一个物理页地址，我们读取elf程序到该地址
        if (sys_read(file, (char *)paddr, curr_size) < curr_size)
        {
            log_printf("read program failed");
            return -1;
        }

        size -= curr_size;
        vaddr += curr_size;
    }
}

/**
 * @brief 加载elf文件
 *
 * @param task 加载文件的任务，当前任务进程
 * @param name 文件名称
 * @param page_dir 加载文件到指定的目录页
 * @return * uint32_t 放回0加载成功
 */
static uint32_t load_elf_file(task_t *task, const char *name, uint32_t page_dir)
{
    Elf32_Ehdr elf_hdr;
    Elf32_Phdr elf_phdr;
    int file = sys_open(name, 0);
    // 打开失败 file实际为文件描述符，小于0失败，大于等于0正常
    if (file < 0)
    {
        log_printf("open failed. %s", name);
        goto load_failed;
    }

    int cnt = sys_read(file, (char *)&elf_hdr, sizeof(Elf32_Ehdr));
    // 读取失败
    if (cnt < sizeof(Elf32_Ehdr))
    {
        log_printf("elf hdr size small. size=%d", cnt);
        goto load_failed;
    }
    // 可以做很多检查在加载之前
    // 拷贝elf的文件,检查文件开头标志是否为.ELF
    // 源码拷贝以下好吧
    // check the elf file header, if not ok! return 0;
    if ((elf_hdr.e_ident[0] != 0x7f) || (elf_hdr.e_ident[1] != 'E') ||
        (elf_hdr.e_ident[2] != 'L') || (elf_hdr.e_ident[3] != 'F'))
    {
        goto load_failed;
    }

    // 获取文件偏移
    uint32_t e_phoff = elf_hdr.e_phoff;
    // 遍历头文件
    for (int i = 0; i < elf_hdr.e_phnum; i++, e_phoff += elf_hdr.e_phentsize)
    {
        // 调整文件头
        if (sys_lseek(file, e_phoff, 0) < 0)
        {
            log_printf("read file failed.");
            goto load_failed;
        }
        // 读取表项
        // 调整文件头后久可以在文件头处读取一个程序头表项
        cnt = sys_read(file, (char *)&elf_phdr, sizeof(Elf32_Phdr));
        if (cnt < sizeof(Elf32_Phdr))
        {
            log_printf("elf hdr size small. size=%d", cnt);
            goto load_failed;
        }
        // 读取的程序头不合理
        if ((elf_phdr.p_type != 1) || (elf_phdr.p_vaddr < MEMORY_TASK_BASE))
        {
            continue;
        }
        // 加载文件
        int err = load_phdr(file, &elf_phdr, page_dir);
        if (err < 0)
        {
            log_printf("read program failed.");
            goto load_failed;
        }

        // 我们依次扫描表现，通过我们连接脚本的设置，加载时heap_start每次指向表项的末端地址，
        // 最后同样指向末端地址，我们依次扫描.text .redata .data .bss 最最后我们的栈指向了
        // .bss的末端地址好吧!栈在0xe0000000我们定义了好吧
        // 空闲经常运行在特权级0，不会动栈的，所以就这样把！
        task->heap_start = elf_phdr.p_vaddr + elf_phdr.p_memsz;
        task->heap_end = task->heap_start;
    }

    sys_close(file);

    return elf_hdr.e_entry;
load_failed:
    if (file)
    {
        sys_close(file);
    }
    return 0;
}
/**
 * @brief 赋值memory函数的参数
 *
 * @param to 目的地址
 * @param page_dir 目录页
 * @param argc
 * @param argv
 * @return * int
 */
static int copy_args(char *to, uint32_t page_dir, int argc, char **argv)
{
    // task参数结构体 main函数
    task_args_t task_args;
    task_args.argc = argc;
    // 不需要返回，这是shell程序,指针的地址argv[0]的地址
    task_args.argv = (char **)(to + sizeof(task_args_t));
    // 跳过 task_args_t 结构体，argv指针表（sizeof(char *) * argc）
    char *dest_arg = to + sizeof(task_args_t) + sizeof(char *) * (argc+1);
    // 跳过 task_args_t 结构体 , 获取argv指针表地址的线性地址，直接对物理地址进行操作
    char **dest_arg_tb = (char **)memory_get_paddr(page_dir, (uint32_t)(to + sizeof(task_args_t)));
    for (int i = 0; i < argc; i++)
    {
        char *from = argv[i];
        // 结束符也算
        int len = kernel_strlen(from) + 1;
        // 字符串低地址放字符串的开头，指针同样指向开头
        int err = memory_copy_uvm_data((uint32_t)dest_arg, page_dir, (uint32_t)from, len);
        ASSERT(err >= 0);
        dest_arg_tb[i] = dest_arg;
        dest_arg += len;

        // 字符串的拷贝，内存页还是比较大，忽略不在同一页的情况
    }
    // for循环完成字符串的拷贝与字符指针表的填写，现在拷贝task_args结构体
    // 实际上应该不可能存在无参的情况，已经将执行文件名作为第一个参数了
    if(argc>0){
        dest_arg_tb[argc] = '\0';
    }
    return memory_copy_uvm_data((uint32_t)to, page_dir, (uint32_t)&task_args, sizeof(task_args));
}
/**
 * @brief 处理参数，将参数数组第一个参数值调整文文件名
 */
void handle_argv(int argc, char **new_argv, char **argv){
    // int new_argc = argc+1;
        // *argc += 1;
    for (int i = 1; i < argc; i++)
    {
        new_argv[i] = argv[i - 1];
    }
    return ;
}
/**
 * @brief 加载一个进程
 * 这个比较复杂，argv/name/env都是原进程空间中的数据，execve中涉及到页表的切换
 * 在对argv和name进行处理时，会涉及到不同进程空间中数据的传递。
 */
int sys_execve(char *name, char **argv, char **env)
{
    // 通过execve加载磁盘上的文件
    // 先做一个只能读取shell.elf的文件系统
    //     为了读取elf文件，需要文件接口，使用一个简单的文件系统实现
    // 子进程创建后一个进程执行execve给该进程创建系统调用接口
    // 建立页表，加载文件，添加文件相关的文件

    task_t *task = task_current();
    kernel_strncpy(task->name, get_file_name(name), TASK_NAME_SIZE);
    uint32_t old_page_dir = task->tss.cr3;
    uint32_t new_page_dir = memory_create_uvm();
    if (!new_page_dir)
    {
        goto exec_failed;
    }
    //加载文件表头 扫描elf文件的表头,加载文件到内存中
    // name就是路径
    uint32_t entry = load_elf_file(task, name, new_page_dir);
    if (entry == 0)
    {
        goto exec_failed;
    }
    // 申请内存栈
    // 注意，这个时用户栈，不是内核栈，frame的时内核栈，两者不相关
    uint32_t stack_top = MEM_TASK_STACK_TOP - MEM_TASK_ARG_SIZE;
    int err = memory_alloc_for_page_dir(new_page_dir, MEM_TASK_STACK_TOP - MEM_TASK_STACK_SIZE, MEM_TASK_STACK_SIZE, PTE_P | PTE_U | PTE_W);
    if (err < 0)
    {
        goto exec_failed;
    }

    int argc = strings_count(argv);
    // 如果数组采用动态数组的话，会对当前的栈造成破坏
    // 将执行的文件名称添加到参数中
    char *new_argv[MAIN_FUNC_PARAM_COUNT] = {};
    new_argv[0] = get_file_name(name);
    int new_argc =argc+1;
    handle_argv(new_argc, new_argv, argv);

    // kernel_strncpy(argv[argc], get_file_name(name), TASK_NAME_SIZE);
    err = copy_args((char *)stack_top, new_page_dir, new_argc, new_argv);
    if (err < 0)
    {
        goto exec_failed;
    }

    syscall_frame_t *frame = (syscall_frame_t *)(task->tss.esp0 - sizeof(syscall_frame_t));
    // 修改栈空间，主要修改eip与栈
    frame->eip = entry;
    frame->eax = frame->ebx = frame->ecx = frame->edx = 0;
    frame->ebp = frame->esi = frame->edi = 0;
    frame->eflags = EFLAGS_DEFAULT | EFLAGS_IF;
    frame->esp = stack_top - (SYSCALL_PARAM_COUNT * sizeof(uint32_t));

    // 当前进程设置取得新的映射页目录表
    task->tss.cr3 = new_page_dir;
    // 更新页目录表，当前进程
    mmu_set_page_dir(new_page_dir);
    // 销毁久表的映射关系
    memory_destory_uvm(old_page_dir);
    return 0;

exec_failed:
    if (new_page_dir)
    {
        task->tss.cr3 = old_page_dir;
        mmu_set_page_dir(old_page_dir);
        memory_destory_uvm(new_page_dir);
    }
    return -1;
}
/**
 * @brief 放回指定文件索引fd（文件描述符）指向的文件
 */
file_t *task_file(int fd)
{
    // fd合法性检测
    if ((fd >= 0) && (fd < TASK_OFILE_NR))
    {
        file_t *file = task_current()->file_table[fd];
        return file;
    }
    return (file_t *)0;
}
/**
 * @brief 为指定文件添加进当前进程的文件列表中
 * @return * int 文件列表索引
 */
int task_alloc_fd(file_t *file)
{
    task_t *task = task_current();
    // 遍历找到空闲表项并分配
    for (int i = 0; i < TASK_OFILE_NR; i++)
    {
        file_t *p = task->file_table[i];
        if (p == (file_t *)0)
        {
            task->file_table[i] = file;
            return i;
        }
    }
    return -1;
}
/**
 * @brief 删除指定文件描述符fd（索引）
 */
void task_remove_fd(int fd)
{
    if ((fd >= 0) && (fd < TASK_OFILE_NR))
    {
        task_current()->file_table[fd] = (file_t *)0;
    }
}
